/*
 * Lemming.java
 *
 * Created on 2007. mC!rcius 12., 16:50
 *
 */

package laborhazi;

//import	java.io.*;

/**
 * The Lemming class represent the model of the lemming in the lemmings 
 * game thus having leading role. Its functional analysis is found in the 
 * analysis modell documentation
 * @author nyuszik
 */
public class Lemming extends FieldElement {
    /* stores the current direction -1: dontmove; 0: left; 1: right*/
    private int direction = 1;
    /* stores the dying energy*/
    private int dyingEnergy = 6;
    /* stores current energy*/
    private int energy = 0;
    /*  stores the old direction for the animation */
    //private int oldDir;
    /*  current position on the map */
    private int[] position = null;
    private int[] oldPos = null;
    /*  the used set of tools */
    private Tool[] tools = null;
    /*  the falling state is set here */
    private boolean falling = false, drilling = false, parachuting = false, erecting = false, exploding = false;
    private int explodeCounter;
    private int counter = 0;
    /*  the reference to the current map */
    private Field map;
    public String name; //temporary for skeleton
    public Test test; //temporary for skeleton
    private boolean current = true;
    private boolean dead = false;
    
    /** 
     * Temporary for skeleton 
     * @param number the "name" of the lemming for testing purpose
     * @param t actual Test object 
     */
    public Lemming (int number, Field f, Test t, int dir){
    	//TODO View.addAnim(new LemmingAnim(this));
    	position = new int[2];
    	test = t;
        map = f;
        direction = dir;
        counter = number;
        tools = new Tool[3];
        current = map.getCurrent();
    }
    public Lemming (int number, Field f, int dir){
    	position = new int[2];
        map = f;
        direction = dir;
        counter = number;
        tools = new Tool[3];
    }
    public Lemming (int number, Test t){
       	position = new int[2];
        test = t;
        counter = number;
    }
    public int getCount() {
    	return counter;
    }
    public String toString() {
    	String toolString;
    	toolString = new String();
    	toolString = " tools: ";
    	if (hasTool(1)) {
    		toolString = toolString + "Blocker ";
    	}
    	if (hasTool(2)) {
    		toolString = toolString + "Parachute ";
    	}
    	if (hasTool(3)) {
    		toolString = toolString + "Erector ";
    	}
    	if (hasTool(4)) {
    		toolString = toolString + "Drill ";
    	}
    	if (hasTool(5)) {
    		toolString = toolString + "Explosive ";
    	}
    	return(counter + ". lemming: " + position[0] + " " + position[1] + toolString);
    }
    /** 
     * Lemming constructor
     * @param home actual Field object 
     */
    public Lemming(Field home) {
    	position = new int[2];
        map = home;
    }
    /** 
     * Lemming constructor for Testing
     * @param number the "name" of the lemming for testing purpose
     * @param t actual Test object
     * @param home actual Field object 
     */
     public Lemming (int number, Field home){
    	position = new int[2];
        map = home;
        name = new String(String.valueOf(number));
     }
     private boolean isSolid(int[] position) {
    	 if(map.check(position) == null) {
    		 return false;
    	 } else {
    		 if (map.check(position) instanceof Stone ||
    				 map.check(position) instanceof StartField ) {
    			 return true;
    		 } else return false;
    	 }
     }
     private boolean isBlocker(int[] position) {
    	 if (map.check(position) == null) {
    		 return false;
    	 } else {
    		 if (map.check(position).isBlocker() == true) {
    			 return true;
    		 } else {
    			 return false;
    		 }
    	 }
     }
     /* 
      * removes if a lemming has a type of tool
      */
     public void removeTool(int toolNum) {
    	 for (int i = 0; i < 3; i++) {
    		 if (tools[i] != null) {
		    	 switch (toolNum) {
					case 1: {
						if (tools[i] instanceof Barricade) tools[i] = null;
						break;
						}
					case 2: {
						if (tools[i] instanceof Parachute) tools[i] = null;
						break;
						}
					case 3: {
						if (tools[i] instanceof Erector) tools[i] = null;
						break;
						}
					case 4: {
						if (tools[i] instanceof Drill) tools[i] = null;
						break;
						}
					case 5: {
						if (tools[i] instanceof Explosive) tools[i] = null;
						break;
						}
					}
    		 }
    	 }
     }
     /* 
      * check if a lemming has a type of tool
      */
     boolean hasTool(int toolNum) {
    	 for (int i = 0; i < 3; i++) {
    		 if (tools[i] != null) {
		    	 switch (toolNum) {
					case 1: {
						if (tools[i] instanceof Barricade) return true;
						break;
						}
					case 2: {
						if (tools[i] instanceof Parachute) return true;
						break;
						}
					case 3: {
						if (tools[i] instanceof Erector) return true;
						break;
						}
					case 4: {
						if (tools[i] instanceof Drill) return true;
						break;
						}
					case 5: {
						if (tools[i] instanceof Explosive) return true;
						break;
						}
					}
    		 }
    	 }
    	 return false;
     }
    /** 
     * check fields surrounding and decide the next step of the Lemming
     */
    public void update(boolean newRound) {
    	int[] calcPos = new int[2];
    	int dir;
    	if(current != newRound) return ;
    	else {
    		oldPos = new int[2];
    		oldPos[0] = position[0];
    		oldPos[1] = position[1];
    		setDrilling(false);
    		setParachuting(false);
    		setExploding(false);
    		setErecting(false);
    		if (direction == 0) {
	    		dir = -1;
	    	} else dir = 1;
	    	//first we check if there is a stone under the lemming, if not, we set the faling to true (not yet falling)
	    	calcPos[0] = position[0];
	    	calcPos[1] = position[1] - 1;
    		if (!isSolid(calcPos)) {
	        	falling = true;
	        } else falling = false;
    		useTool();
    		if(!dead) {
	    		//if we have a barricade we dont step
	    		if (hasTool(1)) {
	    			dontStep();
	    		} else {
	    			//we dont have a barricade we should step
			    	current = !current;
			    	if (direction == 0) {
			    		dir = -1;
			    	} else dir = 1;
			    	//first we check if there is a stone under the lemming, if not, we fall
			    	calcPos[0] = position[0];
			    	calcPos[1] = position[1] - 1;
			        if (!isSolid(calcPos)) {
			        	if(isFinish(position) == true) {
			        		lemmingFinished();
			        	}
			        	falling = true;
			        	fall();
			        } else {
			        	//if the lemming was falling, and had too much energy we turn it into stone
			        	if (!hasTool(2) && energy > dyingEnergy) {
			        		goRock();
			        	} else {
			        		energy = 0;
				        	falling = false;
				        	//what is in front of him, lets find out!
				        	calcPos[0] = position[0] + dir;
				        	calcPos[1] = position[1];
				        	if(isFinish(position) == true) {
				        		lemmingFinished();
				        	} else {
					        	if(!isBlocker(calcPos) && !isSolid(calcPos)) {
					        		//nothing is in front of us, lets go that way
					        		stepOn();
					        	} else {
					        		if (isSolid(calcPos) && !isBlocker(calcPos)) {
					        			//there is something in front of us, not a blocker lemming, maybe we can climb onto it
					        			calcPos[0] = position[0] + dir;
					                	calcPos[1] = position[1] + 1;
					                	if(!isSolid(calcPos) && !isBlocker(calcPos)) {
					                		//there is something above us, not a blocker lemming, or stone
						        			calcPos[0] = position[0];
						                	calcPos[1] = position[1] + 1;
						                	if(!isSolid(calcPos) && !isBlocker(calcPos)) {
						                		stepOnUp();
						                	} else tryTurnAround();
					                	} else tryTurnAround();
					        		} else tryTurnAround();
					        	}
					        	}
			        	}
			        }
	    		}
    		}
    	}
    }
    
    //Something is in front of the lemming, should turn around if can
    private void tryTurnAround() {
    	int[] calcPos = new int[2];
    	int dir;
    	if (direction == 0) {
    		dir = -1;
    	} else dir = 1;
    	calcPos[0] = position[0] - dir;
    	calcPos[1] = position[1];
    	if(!isBlocker(calcPos) && !isSolid(calcPos)) {
    		//nothing is in front of us, lets go that way
    		stepBack();
    	} else {
    		if (isSolid(calcPos)) {
    			//there is something in front of us, not a blocker lemming, maybe we can climb onto it
    			calcPos[0] = position[0] - dir;
            	calcPos[1] = position[1] + 1;
            	if(!isSolid(calcPos) && !isBlocker(calcPos)) {
            		calcPos[0] = position[0];
                	calcPos[1] = position[1] + 1;
                	if(!isSolid(calcPos) && !isBlocker(calcPos)) {
                		stepBackUp();
                	} else dontStep();
            	} else dontStep();
    		} else dontStep();
    	}
    }
    /*
     * useTool uses the available tools
     */
    private void useTool() {
    	for (int i = 0; i < 3; i++) {
    		if (tools[i] != null){
    			tools[i].use(this);
    		}
    	}
    }
    /*
     * We turn the lemming into a rock
     */
    private void goRock() {
    	if (map.check(position) instanceof SetLemming) {
    		((SetLemming)map.check(position)).goRock();
    		/*map.eraseSet(position);
    		System.err.println("TEST-out---------------");
    		map.put(new Stone(map), position);
    		*/
    		}
    	else {
    		if (dead == false) {
	    	map.decLemming();
	    	if(map.getLemmingNum() == 0) {
	    		map.viewer.game.end();
	    		} else {
	    		dead = true;
		    	map.remove(this, position);
		    	map.put(new Stone(map), position);
		    	}
	    	}
    	}
    }
    
    /** 
     * get a tool from toolbar for further usage
     * @return returns TRUE only if the tool can be used here
     * @param tool the reference of the tool given to the Lemming     
     */
    public boolean recieveTool(Tool tool) {
        if (checkUsability(tool) == true) {
        	for (int i = 0; i < 3; i++) {
        		if (tools[i] == null) {
        			tools[i] = tool;
        			break;
        		}
        	}
        	// NONONO TODO ??
            //tool.use(this);
            System.out.println("recieved usable "+ tool.toString()+ " " + this.toString());
            return true;
        } else {
        	System.out.println("recieved not usable "+ tool.toString()+ " " + this.toString());
        	return false;
        }
    } 
    
    private void lemmingFinished() {
    	map.decLemming();
    	map.getFinish().increase();
    	if(map.getLemmingNum() == 0) {
    		map.viewer.game.end();
    	}
    	else {
    		map.remove(this, position);
    	}
    }
    public boolean isDead() {
    	return dead;
    }
    private boolean isFinish(int[] pos) {
    	int[] myPos;
    	myPos = new int[2];
    	
    	myPos[0] = pos[0] + 1;
    	myPos[1] = pos[1] + 0;
    	if(map.check(myPos) != null && map.check(myPos) instanceof FinishField) {
    		return true;
    	}
    	myPos[0] = pos[0] + 0;
    	myPos[1] = pos[1] + 1;
    	if(map.check(myPos) != null && map.check(myPos) instanceof FinishField) {
    		return true;
    	}
    	myPos[0] = pos[0] - 1;
    	myPos[1] = pos[1] + 0;
    	if(map.check(myPos) != null && map.check(myPos) instanceof FinishField) {
    		return true;
    	}
    	myPos[0] = pos[0] + 0;
    	myPos[1] = pos[1] - 1;
    	if(map.check(myPos) != null && map.check(myPos) instanceof FinishField) {
    		return true;
    	}
    	return false;
    }
    
    /** 
     * checks wheather the lemming is a blocker one or a none blocker one
     * @return is only TRUE if the Lemming is a blockhead
     */
    public boolean isBlocker() {
    	int max = tools.length;
    	boolean isIt = false;
    	for(int i = 0; i < max; i++) {
    		if (tools[i] != null && (tools[i] instanceof Barricade)) isIt = true;
    	}
    	if (isIt) {
    		return true;
    	} else return false;
    }
    /** 
     * invert the direction of the Lemming
     */
    private void stepBack() {
       System.out.println("turnaround" + this.toString());
       int[] newPos = new int[2];
       int newWay;
       if (direction == 0) {
    	   direction = 1;
    	   newWay = -1;
       } else {
    	   direction = 0;
    	   newWay = 1;
       }
       newPos[0] = position[0] - newWay;
       newPos[1] = position[1];
       map.remove(this, position);
       map.put(this, newPos);
    }
    /** 
     * invert the direction of the Lemming, and step up
     */
    private void stepBackUp() {
       System.out.println("turnaround" + this.toString());
       int[] newPos = new int[2];
       int newWay;
       if (direction == 0) {
    	   direction = 1;
    	   newWay = -1;
       } else {
    	   direction = 0;
    	   newWay = 1;
       }
       newPos[0] = position[0] - newWay;
       newPos[1] = position[1] + 1;
       map.remove(this, position);
       map.put(this, newPos);
    }
    /** 
     * increase the momentum of the Lemming when he/she falls a block
     */
    private void increaseEnergy() {
    	energy++;
    }
    /** stops the Lemming so he/she becomes a blocked lemming aka Barricade*/
    public void stop() {
    	System.out.println("stopped: " + this.toString());
    	direction = -1;
    }
    /** 
     * the lemming steps forward in old direction
     */
    private void stepOn() {
    	int[] newPos = new int[2];
        int newWay;
        if (direction == 0) {
     	   newWay = -1;
        } else newWay = 1;
        newPos[0] = position[0] + newWay;
        newPos[1] = position[1];
        map.remove(this, position);
        map.put(this, newPos);
    }
    /** 
     * the lemming steps forward and up in old direction
     */
    private void stepOnUp() {
    	int[] newPos = new int[2];
        int newWay;
        if (direction == 0) {
     	   newWay = -1;
        } else newWay = 1;
        newPos[0] = position[0] + newWay;
        newPos[1] = position[1] + 1;
        map.remove(this, position);
        map.put(this, newPos);
    }
    /** 
     * the stands - no move
     */
    private void dontStep() {
    	//direction = -1;
    }
    /** Checks the current usability of a tool
     * PRIVATE set to public for testing 
     * @param tool this Tool is checked
     */
    public boolean checkUsability(Tool tool) {
    	if (tool instanceof Barricade) {
    		if (hasTool(1)) return false;
    		if (falling) return false;
    		for (int i = 0; i < 3; i++) {
				if (tools[i] instanceof Erector || tools[i] instanceof Drill) {
					tools[i] = null;
				}
			}
    		return true;
    	} else if (tool instanceof Parachute) {
    		if (hasTool(2)) return false;
    		else return true;
    	} else if (tool instanceof Erector) {
    		if (hasTool(1)) return false;
    		if (falling) return false;
    		for (int i = 0; i < 3; i++) {
				if (tools[i] instanceof Erector || tools[i] instanceof Drill) {
					tools[i] = null;
				}
			}
    		return true;
    	} else if (tool instanceof Drill) {
    		if (hasTool(1)) return false;
    		if (falling) return false;
    		for (int i = 0; i < 3; i++) {
				if (tools[i] instanceof Erector || tools[i] instanceof Drill) {
					tools[i] = null;
				}
			}
    		return true;
    	} else if (tool instanceof Explosive) {
    		if (hasTool(5)) return false;
    		return true;
    	}
    	return false;
    }
    /** 
     * blow up
     */
    public void blow() {
    	if(!dead) {
	    	map.decLemming();
	    	if(map.getLemmingNum() == 0) {
	    		map.viewer.game.end();
	    	}
	    	dead = true;
	    	erase();
    	}
    }
    protected void erase() {
    	map.remove(this, position);
    }
    /** 
     * changes the Lemming to a falling Lemming
     */
    private void fall() {
        System.out.println("falling " + this.toString());
        if (hasTool(2)) {
        	setParachuting(true);
        }
        increaseEnergy();
        int[] newPos = new int[2];
        newPos[0] = position[0];
        newPos[1] = position[1] - 1;
       /* if (map.check(newPos) instanceof Lemming && ((Lemming)map.check(newPos)).getDir() != -1) {*/
        	map.remove(this, position);
        	map.put(this, newPos);
        //}
    }
    /** 
     * this one is to give back the position of the lemming for the tool 
     * ie. the Drill
     * @return the Lemming's position on the map is returned
     */
    public int[] getPosition() {
        return(position);
    }
    /** 
     * this one is to give back the direction of the lemming for the tool 
     * ie. the Drill
     * @return the Lemming's direction on the map is returned
     */
    public int getDir() {
        return(direction);
    }
    /** 
     * this one is to set the position of the lemming
     * ie. the Drill
     * @param position the Lemming's position on the map is returned
     */
    public void setPos(int[] poss) {
    	position[0] = poss[0];
     	position[1] = poss[1];
     }
    /** 
     * checks if the lemming is falling
     * @return true if the lemming is falling
     */
    public boolean isFalling() {
    	if (falling) {
    		return true;
    	} else return false;
    }
    /**
     * sets the lemmings to drill or not to drill
     * @param setit the state to be set
     */
    public void setDrilling(boolean setit) {
    	drilling = setit;
    }
    /**
     * checks if the lemming is drilling
     * @return the state
     */
    public boolean isDrilling() {
    	return drilling;
    }
    /**
     * sets the lemmings this state
     * @param setit the state to be set
     */
    public void setParachuting(boolean setit) {
    	parachuting = setit;
    }
    /**
     * checks if the lemming is parachuting
     * @return the state
     */
    public boolean isParachuting() {
    	return parachuting;
    }
    /**
     * sets the lemmings this state
     * @param setit the state to be set
     */
    public void setErecting(boolean setit) {
    	erecting = setit;
    }
    /**
     * checks if the lemming is erecting
     * @return the state
     */
    public boolean isErecting() {
    	return erecting;
    }
    /**
     * sets the lemmings this state
     * @param setit the state to be set
     */
    public void setExploding(boolean setit) {
    	exploding = setit;
    }
    /**
     * checks if the lemming is exploding
     * @return the state
     */
    public boolean isExploding() {
    	return exploding;
    }
    /**
     * sets the explode counter
     * @param setit the time to be set
     */
    public void setExplodingTime(int setit) {
    	explodeCounter = setit;
    }
    /**
     * returns the time left until blow
     * @return the state
     */
    public int getExplodeCounter() {
    	return explodeCounter;
    }
    /**
     * returns old position
     * @return the old position
     */
    public int[] getOldPos() {
    	if (oldPos == null) {
    		return position;
    	} else return oldPos;
    }
}
